

%🍁% \chapter{Case study: word play  |  文字游戏}
\chapter{案例研究：文字游戏}
\label{wordplay}

%🍁% This chapter presents the second case study, which involves
%🍁% solving word puzzles by searching for words that have certain
%🍁% properties.  For example, we'll find the longest palindromes
%🍁% in English and search for words whose letters appear in
%🍁% alphabetical order.  And I will present another program development
%🍁% plan: reduction to a previously solved problem.

这一章将介绍第二个案例研究， 即通过查找具有特定属性的单词来解答字谜游戏。
例如， 我们将找出英文中最长的回文单词， 以及字符按照字符表顺序出现的单词。
另外， 我还将介绍另一种程序开发方法： 简化为之前已解决的问题。

%🍁% \section{Reading word lists  |  读取单词列表}
\section{读取单词列表}
\label{wordlist}

%🍁% For the exercises in this chapter we need a list of English words.
%🍁% There are lots of word lists available on the Web, but the one most
%🍁% suitable for our purpose is one of the word lists collected and
%🍁% contributed to the public domain by Grady Ward as part of the Moby
%🍁% lexicon project (see \url{http://wikipedia.org/wiki/Moby_Project}).  It
%🍁% is a list of 113,809 official crosswords; that is, words that are
%🍁% considered valid in crossword puzzles and other word games.  In the
%🍁% Moby collection, the filename is {\tt 113809of.fic}; you can download
%🍁% a copy, with the simpler name {\tt words.txt}, from
%🍁% \url{http://thinkpython2.com/code/words.txt}.
\index{Moby Project}  \index{crosswords}

为了完成本章的习题， 我们需要一个英语单词的列表。
网络上有许多单词列表， 但是最符合我们目的列表之一是由 Grady
Ward收集并贡献给公众的列表， 这也是Moby词典项目的一部分\footnote{(见：\url{http://wikipedia.org/wiki/Moby_Project}}。
它由 113,809 个填字游戏单词组成， 即在填字游戏以及其它文字游戏中被认为有效的单词。
在 Moby 集合中， 该列表的文件名是 \li{113809of.fic} ；你可以从 \href{http://thinkpython.com/code/words.txt}{这里} 下载一个拷贝， 文件名已被简化为 \li{words.txt}。

\index{Moby Project}  \index{crosswords}
\index{维基百科}

%🍁% This file is in plain text, so you can open it with a text
%🍁% editor, but you can also read it from Python.  The built-in
%🍁% function {\tt open} takes the name of the file as a parameter
%🍁% and returns a {\bf file object} you can use to read the file.

该文件是纯文本， 因此你可以用一个文本编辑器打开它， 但是你也可以从 Python 中读取它。   内建函数 \li{open} 接受文件名作为形参， 并返回一个 {\em 文件对象} (file object)， 你可以使用它读取该文件。

\index{open function}  \index{function!open}
\index{plain text}  \index{text!plain}
\index{object!file}  \index{file object}

\begin{lstlisting}
>>> fin = open('words.txt')
\end{lstlisting}

%🍁% %
%🍁% {\tt fin} is a common name for a file object used for input.  The file
%🍁% object provides several methods for reading, including {\tt readline},
%🍁% which reads characters from the file until it gets to a newline and
%🍁% returns the result as a string:
\index{readline method}  \index{method!readline}
\index{readline 方法}  \index{方法!readline}

\li{`fin} 是输入文件对象的一个常用名。
该文件对象提供了几个读取方法， 包括 \li{readline}， 其从文件中读取字符直到碰到新行， 并将结果作为字符串返回：
\index{method!readline}

\begin{lstlisting}
>>> fin.readline()
'aa\r\n'
\end{lstlisting}

%🍁% %
%🍁% The first word in this particular list is ``aa'', which is a kind of
%🍁% lava.  The sequence \verb"\r\n" represents two whitespace characters,
%🍁% a carriage return and a newline, that separate this word from the
%🍁% next.

在此列表中， 第一个单词是 ``aa''， 它是一类熔岩的名称。   序列 \li{\r\n} 代表两个空白字符， 回车和换行， 它们将这个单词和下一个分开。

%🍁% The file object keeps track of where it is in the file, so
%🍁% if you call {\tt readline} again, you get the next word:

此文件对象跟踪它在文件中的位置，
所以如果你再次调用 \li{readline}， 你获得下一个单词：

\begin{lstlisting}
>>> fin.readline()
'aah\r\n'
\end{lstlisting}

%🍁% %
%🍁% The next word is ``aah'', which is a perfectly legitimate
%🍁% word, so stop looking at me like that.
%🍁% Or, if it's the whitespace that's bothering you,
%🍁% we can get rid of it with the string method {\tt strip}:

下一个单词是 ``aah''， 不要惊讶， 它是一个完全合法的单词。
或者， 如果空格困扰了你， 我们可以用字符串方法 \li{strip} 删掉它：
\index{strip method}  \index{method!strip}
\index{strip 方法}  \index{方法!strip}


\begin{lstlisting}
>>> line = fin.readline()
>>> word = line.strip()
>>> word
'aahed'
\end{lstlisting}

%🍁% %
%🍁% You can also use a file object as part of a {\tt for} loop.
%🍁% This program reads {\tt words.txt} and prints each word, one
%🍁% per line:

你也可以将文件对象用做 \li{for} 循环的一部分。
此程序读取 \li{words.txt} 并打印每个单词， 每行一个：
\index{open function}  \index{function!open}

\begin{lstlisting}
fin = open('words.txt')
for line in fin:
    word = line.strip()
    print(word)
\end{lstlisting}

%
%🍁% \section{Exercises  |  练习}
\section{练习}

%🍁% There are solutions to these exercises in the next section.
%🍁% You should at least attempt each one before you read the solutions.

\hyperref[search]{下一节}将给出了这些习题的答案。
在你看答案之前， 应该试着解答一下。

\begin{exercise}
%🍁% Write a program that reads {\tt words.txt} and prints only the
%🍁% words with more than 20 characters (not counting whitespace).

编程写一个程序， 使得它可以读取 {\em \li{words.txt}} ， 然后只打印出那些长度超过 20 个字符的单词 (不包括空格)。
\index{whitespace}

\end{exercise}

\begin{exercise}

%🍁% In 1939 Ernest Vincent Wright published a 50,000 word novel called
%🍁% {\em Gadsby} that does not contain the letter ``e''.  Since ``e'' is
%🍁% the most common letter in English, that's not easy to do.

{\em 1939} 年， {\em Ernest Vincent Wright} 出版了一本名为 {\em 《Gadsby》} 的小说，
该小说里完全没有使用字符 {\em ``e''}。   由于 {\em ``e''} 是英文中最常用的字符， 因此写出这本书并不容易。

%🍁% In fact, it is difficult to construct a solitary thought without using
%🍁% that most common symbol.  It is slow going at first, but with caution
%🍁% and hours of training you can gradually gain facility.

事实上， 不使用这个最常用的字符来构建一个简单的想法都是很难的。
开始进展缓慢， 但是经过有意识的、长时间的训练， 你可以逐渐地熟练。

%🍁% All right, I'll stop now.

好啦， 不说题外话了， 我们开始编程练习。

%🍁% Write a function called \verb"has_no_e" that returns {\tt True} if
%🍁% the given word doesn't have the letter ``e'' in it.

写一个叫做 {\em \li{has_no_e}} 的函数， 如果给定的单词中不包含字符 {\em ``e''}， 返回 {\em \li{True}} 。

%🍁% Modify your program from the previous section to print only the words
%🍁% that have no ``e'' and compute the percentage of the words in the list
%🍁% that have no ``e''.

修改上一节中的程序， 只打印不包含 {\em ``e''} 的单词， 并且计算列表中不含 {\em ``e''} 单词的比例。
\index{lipogram}

\end{exercise}


\begin{exercise}

%🍁% Write a function named {\tt avoids}
%🍁% that takes a word and a string of forbidden letters, and
%🍁% that returns {\tt True} if the word doesn't use any of the forbidden
%🍁% letters.

编写一个名为 {\em \li{avoids}} 的函数， 接受一个单词和一个指定禁止使用字符的字符串，
如果单词中不包含任意被禁止的字符， 则返回 {\em \li{True}} 。

%🍁% Modify your program to prompt the user to enter a string
%🍁% of forbidden letters and then print the number of words that
%🍁% don't contain any of them.
%🍁% Can you find a combination of 5 forbidden letters that
%🍁% excludes the smallest number of words?

修改你的程序， 提示用户输入一个禁止使用的字符， 然后打印不包含这些字符的单词的数量。
你能找到一个5个禁止使用字符的组合， 使得其排除的单词数目最少么？

\end{exercise}



\begin{exercise}

%🍁% Write a function named \verb"uses_only" that takes a word and a
%🍁% string of letters, and that returns {\tt True} if the word contains
%🍁% only letters in the list.  Can you make a sentence using only the
%🍁% letters {\tt acefhlo}?  Other than ``Hoe alfalfa?''

编写一个名为 {\em \li{uses_only}} 的函数， 接受一个单词和一个字符串。
如果该单词只包括此字符串中的字符， 则返回 {\em \li{True}}。
你能只用 {\em \li{"acefhlo"}} 这几个字符造一个句子么？ 除了 ``Hoe alfalfa'' 外。

\end{exercise}


\begin{exercise}

%🍁% Write a function named \verb"uses_all" that takes a word and a
%🍁% string of required letters, and that returns {\tt True} if the word
%🍁% uses all the required letters at least once.  How many words are there
%🍁% that use all the vowels {\tt aeiou}?  How about {\tt aeiouy}?

编写一个名为 {\em \li{uses_all}} 的函数， 接受一个单词和一个必须使用的字符组成的字符串。
如果该单词包括此字符串中的全部字符至少一次， 则返回 {\em \li{True}}。
你能统计出多少单词包含了所有的元音字符 {\em \li{aeiou}}吗？如果换成 {\em \li{aeiouy}} 呢？

\end{exercise}


\begin{exercise}

%🍁% Write a function called \verb"is_abecedarian" that returns
%🍁% {\tt True} if the letters in a word appear in alphabetical order
%🍁% (double letters are ok).
%🍁% How many abecedarian words are there?

编写一个名为 {\em \li{is_abecedarian}} 的函数， 如果单词中的字符以字符表的顺序出现(允许重复字符)， 则返回 {\em \li{True}} 。
有多少个具备这种特征的单词？

\index{abecedarian}  \index{字符表}

\end{exercise}



%🍁% \section{Search  ||  搜索}
\section{搜索}
\label{search}
\index{search pattern}  \index{pattern!search}

%🍁% All of the exercises in the previous section have something
%🍁% in common; they can be solved with the search pattern we saw
%🍁% in Section~\ref{find}.  The simplest example is:

前一节的所有习题有一个共同点；都可以用在 \ref{find}~节 中看到的搜索模式解决。

\begin{lstlisting}
def has_no_e(word):
    for letter in word:
        if letter == 'e':
            return False
    return True
\end{lstlisting}

%
%🍁% The {\tt for} loop traverses the characters in {\tt word}.  If we find
%🍁% the letter ``e'', we can immediately return {\tt False}; otherwise we
%🍁% have to go to the next letter.  If we exit the loop normally, that
%🍁% means we didn't find an ``e'', so we return {\tt True}.

\li{for} 循环遍历 \li{word} 中的字符。
如果我们找到字符 \li{"e"}， 那么我们可以马上返回 \li{False} ；
否则我们必须检查下一个字符。
如果我们正常退出循环， 就意味着我们没有找到一个 ``e''， 所以我们返回 \li{True} 。

\index{traversal}

\index{in operator}  \index{operator!in}

%🍁% You could write this function more concisely using the {\tt in}
%🍁% operator, but I started with this version because it
%🍁% demonstrates the logic of the search pattern.

你也可以用 \li{in} 操作符简化上述函数， 但之所以一开始写成这样， 是因为它展示了搜索模式的逻辑。

\index{generalization}

%🍁% {\tt avoids} is a more general version of \verb"has_no_e" but it
%🍁% has the same structure:

\li{avoid}　是一个更通用的 \li{has_no_e} 函数， 但是结构是相同的：

\begin{lstlisting}
def avoids(word, forbidden):
    for letter in word:
        if letter in forbidden:
            return False
    return True
\end{lstlisting}

%🍁% %
%🍁% We can return {\tt False} as soon as we find a forbidden letter;
%🍁% if we get to the end of the loop, we return {\tt True}.

一旦我们找到一个禁止使用的字符， 我们返回 \li{False} ；
如果我们到达循环结尾， 我们返回 \li{True} 。

%🍁% \verb"uses_only" is similar except that the sense of the condition
%🍁% is reversed:

除了检测条件相反以外， 下面 \li{uses_only} 函数与上面的函数很像：

\begin{lstlisting}
def uses_only(word, available):
    for letter in word:
        if letter not in available:
            return False
    return True
\end{lstlisting}

%🍁% %
%🍁% Instead of a list of forbidden letters, we have a list of available
%🍁% letters.  If we find a letter in {\tt word} that is not in
%🍁% {\tt available}, we can return {\tt False}.

这里我们传入一个允许使用字符的列表， 而不是禁止使用字符的列表。
如果我们在 \li{word} 中找到一个不在 \li{available} 中的字符， 我们就可以返回 \li{False} 。

%🍁% \verb"uses_all" is similar except that we reverse the role
%🍁% of the word and the string of letters:

除了将 \li{word} 与所要求的字符的角色进行了调换之外，
下面的 \li{uses_all} 函数也是类似的。

\begin{lstlisting}
def uses_all(word, required):
    for letter in required:
        if letter not in word:
            return False
    return True
\end{lstlisting}

%🍁% %
%🍁% Instead of traversing the letters in {\tt word}, the loop
%🍁% traverses the required letters.  If any of the required letters
%🍁% do not appear in the word, we can return {\tt False}.

该循环遍历需要的字符， 而不是遍历 \li{word} 中的字符。  如果任何要求的字符没出现在单词中， 则我们返回 \li{False} 。

\index{traversal}

%🍁% If you were really thinking like a computer scientist, you would
%🍁% have recognized that \verb"uses_all" was an instance of a
%🍁% previously solved problem, and you would have written:

如果你真的像计算机科学家一样思考， 你可能已经意识到 \li{uses_all} 是前面已经解决的问题的一个实例， 你可能会写成：

\begin{lstlisting}
def uses_all(word, required):
    return uses_only(required, word)
\end{lstlisting}

%🍁% %
%🍁% This is an example of a program development plan called {\bf
%🍁%   reduction to a previously solved problem}, which means that you
%🍁% recognize the problem you are working on as an instance of a solved
%🍁% problem and apply an existing solution.  \index{reduction to a
%🍁%   previously solved problem} \index{development plan!reduction}

这是一种叫做 {\bf 简化为之前已解决的问题\footnote{reduction to a
previously solved problem}} 的程序开发方法的一个示例，
也就是说， 你认识到当前面临的问题是之前已经解决的问题的一个实例，
然后应用了已有的解决方案。


%🍁% \section{Looping with indices  |  使用索引进行循环}
\section{使用索引进行循环}
\index{looping!with indices}  \index{index!looping with}
\index{循环!使用索引}  \index{索引!控制循环}

%🍁% I wrote the functions in the previous section with {\tt for}
%🍁% loops because I only needed the characters in the strings; I didn't
%🍁% have to do anything with the indices.

前一节我用 \li{for} 循环来编写函数， 因为我只需要处理字符串中的字符；
我不必用索引做任

%🍁% For \verb"is_abecedarian" we have to compare adjacent letters,
%🍁% which is a little tricky with a {\tt for} loop:

对于下面的 \li{is_abecedarian} ， 我们必须比较邻接的字符， 用 \li{for} 循环来写的话有点棘手。

\begin{lstlisting}
def is_abecedarian(word):
    previous = word[0]
    for c in word:
        if c < previous:
            return False
        previous = c
    return True
\end{lstlisting}

%🍁% An alternative is to use recursion:

一种替代方法是使用递归：

\begin{lstlisting}
def is_abecedarian(word):
    if len(word) <= 1:
        return True
    if word[0] > word[1]:
        return False
    return is_abecedarian(word[1:])
\end{lstlisting}

%🍁% Another option is to use a {\tt while} loop:

另一中方法是使用 \li{while} 循环：

\begin{lstlisting}
def is_abecedarian(word):
    i = 0
    while i < len(word)-1:
        if word[i+1] < word[i]:
            return False
        i = i+1
    return True
\end{lstlisting}

%🍁% %
%🍁% The loop starts at {\tt i=0} and ends when {\tt i=len(word)-1}.  Each
%🍁% time through the loop, it compares the $i$th character (which you can
%🍁% think of as the current character) to the $i+1$th character (which you
%🍁% can think of as the next).

循环起始于 \li{i=0} ， \li{i=len(word)-1} 时结束。
每次循环， 函数会比较第 $i$ 个字符(可以将其认为是当前字符)
和第 $i+1$ 个字符(可以将其认为是下一个字符)。

%🍁% If the next character is less than (alphabetically before) the current
%🍁% one, then we have discovered a break in the abecedarian trend, and
%🍁% we return {\tt False}.

如果下一个字符比当前的小(字符序靠前)，
那么我们在递增趋势中找到了断点， 即可返回 \li{False} 。

%🍁% If we get to the end of the loop without finding a fault, then the
%🍁% word passes the test.  To convince yourself that the loop ends
%🍁% correctly, consider an example like \verb"'flossy'".  The
%🍁% length of the word is 6, so
%🍁% the last time the loop runs is when {\tt i} is 4, which is the
%🍁% index of the second-to-last character.  On the last iteration,
%🍁% it compares the second-to-last character to the last, which is
%🍁% what we want.

如果到循环结束时我们也没有找到一点错误， 那么该单词通过测试。
为了让你相信循环正确地结束了， 我们用 \li{'flossy'} 这个单词来举例。
它的长度为 6， 因此最后一次循环运行时， \li{i} 是 4， 这是倒数第 2 个字符的索引。
最后一次迭代时， 函数比较倒数第二个和最后一个字符， 这正是我们希望的。
\index{palindrome}

%🍁% Here is a version of \verb"is_palindrome" (see
%🍁% Exercise~\ref{palindrome}) that uses two indices; one starts at the
%🍁% beginning and goes up; the other starts at the end and goes down.

下面是 \li{is_palindrome} 函数的一种版本(详见 练习~\ref{palindrome} )
， 其中使用了两个索引；一个从最前面开始并往前上， 另一个从最后面开始并往下走。

\begin{lstlisting}
def is_palindrome(word):
    i = 0
    j = len(word)-1

    while i<j:
        if word[i] != word[j]:
            return False
        i = i+1
        j = j-1

    return True
\end{lstlisting}

%🍁% Or we could reduce to a previously solved
%🍁% problem and write:

或者， 我们可以把问题简化为之前已经解决的问题， 这样来写:

\index{reduction to a previously solved problem}
\index{development plan!reduction}

\begin{lstlisting}
def is_palindrome(word):
    return is_reverse(word, word)
\end{lstlisting}

%🍁% %
%🍁% Using \verb"is_reverse" from Section~\ref{isreverse}.

使用 \ref{isreverse}~节 中描述的 \li{is_reverse}


%🍁% \section{Debugging  |  调试}
\section{调试}
\index{debugging}  \index{testing!is hard}  \index{program testing}

%🍁% Testing programs is hard.  The functions in this chapter are
%🍁% relatively easy to test because you can check the results by hand.
%🍁% Even so, it is somewhere between difficult and impossible to choose a
%🍁% set of words that test for all possible errors.

程序测试很困难。  本章中介绍的函数相对容易测试， 因为你可以手工检查结果。
即使这样， 选择一可以测试所有可能错误的单词集合， 是很困难的， 介于困难和不可能之间。

%🍁% Taking \verb"has_no_e" as an example, there are two obvious
%🍁% cases to check: words that have an `e' should return {\tt False}, and
%🍁% words that don't should return {\tt True}.  You should have no
%🍁% trouble coming up with one of each.

以 \li{has_no_e} 为例， 有两个明显的用例需要检查：
含有 `e' 的单词应该返回 \li{False}， 不含的单词应该返回 \li{True} 。
你应该可以很容易就能想到这两种情况。

%🍁% Within each case, there are some less obvious subcases.  Among the
%🍁% words that have an ``e'', you should test words with an ``e'' at the
%🍁% beginning, the end, and somewhere in the middle.  You should test long
%🍁% words, short words, and very short words, like the empty string.  The
%🍁% empty string is an example of a {\bf special case}, which is one of
%🍁% the non-obvious cases where errors often lurk.

在每个用例中， 还有一些不那么明显的子用例。
在含有 ``e'' 的单词中， 你应该测试 ``e'' 在开始、结尾以及在中间的单词。
你还应该测试长单词、短单词以及非常短的单词， 如空字符串。
空字符串是一个 {\bf 特殊用例\footnote{special case}} ， 及一个经常出现错误的不易想到的用例。
\index{special case}

%🍁% In addition to the test cases you generate, you can also test
%🍁% your program with a word list like {\tt words.txt}.  By scanning
%🍁% the output, you might be able to catch errors, but be careful:
%🍁% you might catch one kind of error (words that should not be
%🍁% included, but are) and not another (words that should be included,
%🍁% but aren't).

除了你生成的测试用例， 你也可以用一个类似 \li{words.txt} 中的单词列表测试你的程序。   通过扫描输出， 你可能会捕获错误， 但是请注意：
你可能捕获一类错误(包括了不应该包括的单词)却没能捕获另一类错误(没有包括应该包括的单词)。

%🍁% In general, testing can help you find bugs, but it is not easy to
%🍁% generate a good set of test cases, and even if you do, you can't
%🍁% be sure your program is correct.
%🍁% According to a legendary computer scientist:
\index{testing!and absence of bugs}

%🍁% \begin{quote}
%🍁% Program testing can be used to show the presence of bugs, but never to
%🍁% show their absence!
%🍁%
%🍁% --- Edsger W. Dijkstra
%🍁% \end{quote}
%🍁% \index{Dijkstra, Edsger}

一般来讲， 测试能帮助你找到错误， 但是生成好的测试用例并不容易，
并且即便你做到了， 你仍然不能保证你的程序是正确的。  正如一位传奇计算机科学家所说：
\index{testing!and absence of bugs}
\begin{quote}
{\bf 程序测试能用于展示错误的存在， 但是无法证明不存在错误！}

--- Edsger W. Dijkstra
\end{quote}
\index{Dijkstra, Edsger}


%🍁% \section{Glossary  |  术语表}
\section{术语表}

\begin{description}

%🍁% \item[file object:] A value that represents an open file.
\index{file object}  \index{object!file}

\item[文件对象 (file object):] 代表打开文件的变量。
\index{文件对象}  \index{对象!文件}

%🍁% \item[reduction to a previously solved problem:] A way of solving a
%🍁%   problem by expressing it as an instance of a previously solved
%🍁%   problem.
\index{reduction to a previously solved problem}
\index{development plan!reduction}

\item[简化为已解决的问题：] 通过把未知问题简化为已经解决的问题来解决问题的方法。
\index{简化为已解决的问题}  \index{开发计划!简化}

%🍁% \item[special case:] A test case that is atypical or non-obvious
%🍁% (and less likely to be handled correctly).
\index{special case}

\item[特殊用例 (special case):] 一种不典型或者不明显的测试用例(而且很可能无法正确解决的用例)。
\index{special case}

\end{description}


%🍁% \section{Exercises}
\section{练习}

\begin{exercise}
\index{Car Talk}  \index{Puzzler}  \index{double letters}

%🍁% This question is based on a Puzzler that was broadcast on the radio
%🍁% program {\em Car Talk}
%🍁% (\url{http://www.cartalk.com/content/puzzlers}):

这个问题基于广播节目 {\em 《\href{http://www.cartalk.com/content/puzzlers}{Car Talk}》} 上介绍的一个字谜：

%🍁% \begin{quote}
%🍁% Give me a word with three consecutive double letters. I'll give you a
%🍁% couple of words that almost qualify, but don't. For example, the word
%🍁% committee, c-o-m-m-i-t-t-e-e. It would be great except for the `i' that
%🍁% sneaks in there. Or Mississippi: M-i-s-s-i-s-s-i-p-p-i. If you could
%🍁% take out those i's it would work. But there is a word that has three
%🍁% consecutive pairs of letters and to the best of my knowledge this may
%🍁% be the only word. Of course there are probably 500 more but I can only
%🍁% think of one. What is the word?
%🍁% \end{quote}

\begin{quote}
    找出一个包含三个连续双字符的单词。  我将给你一系列几乎能够符合条件但实际不符合的单词。
    比如， {\em committee} 这个单词， {\em c-o-m-m-i-t-t-e-e}。
    如果中间没有 {\em i} 的话， 就太棒了。
    或者 {\em Mississippi} 这个单词: {\em M-i-s-s-i-s-s-i-p-p-i}。
    假如将这些 {\em i} 剔除出去， 就会符合条件。
    但是确实存在一个包含三个连续的单词对， 而且据我了解， 它可能是唯一符合条件的单词。
    当然也可能有 {\em 500} 多个， 但是我只能想到一个。
    那么这个单词是什么？
\end{quote}

%🍁% Write a program to find it.
%🍁% Solution: \url{http://thinkpython2.com/code/cartalk1.py}.

编写一个程序， 找到这个单词。
\href{http://thinkpython2.com/code/cartalk1.py}{参考答案}

\end{exercise}


\begin{exercise}
%🍁% Here's another {\em Car Talk}
%🍁% Puzzler (\url{http://www.cartalk.com/content/puzzlers}):

下面是另一个来自 {\em 《\href{http://www.cartalk.com/content/puzzlers}{Car Talk}》} 的谜题:
\index{Car Talk}  \index{Puzzler}
\index{odometer}  \index{palindrome}

%🍁% \begin{quote}
%🍁% ``I was driving on the highway the other day and I happened to
%🍁% notice my odometer. Like most odometers, it shows six digits,
%🍁% in whole miles only. So, if my car had 300,000
%🍁% miles, for example, I'd see 3-0-0-0-0-0.
%🍁%
%🍁% Now, what I saw that day was very interesting. I noticed that the
%🍁% last 4 digits were palindromic; that is, they read the same forward as
%🍁% backward. For example, 5-4-4-5 is a palindrome, so my odometer
%🍁% could have read 3-1-5-4-4-5.
%🍁%
%🍁% One mile later, the last 5 numbers were palindromic. For example, it
%🍁% could have read 3-6-5-4-5-6.  One mile after that, the middle 4 out of
%🍁% 6 numbers were palindromic.  And you ready for this? One mile later,
%🍁% all 6 were palindromic!
%🍁%
%🍁% The question is, what was on the odometer when I first looked?''
%🍁% \end{quote}

\begin{quote}
    ``有一天， 我正在高速公路上开车， 我偶然注意到我的里程表。  和大多数里程表一样， 它只显示 {\em 6} 位数字的整数英里数。
    所以， 如果我的车开了{\em 300,000} 英里， 我能够看到的数字是{\em :3-0-0-0-0-0}。

    我当天看到的里程数非常有意思。  我注意到后四位数字是回文数；也就是说， 正序读和逆序读是一样的。  例如， {\em 5-4-4-5} 就是回文数。
    所以我的里程数可能是{\em 3-1-5-4-4-5}。

    一英里后， 后五位数字变成了回文数。  例如， 里程数可能变成了是 {\em 3-6-5-4-5-6} 。  又过了一英里后， {\em 6} 位数字的中间四位变成了回文数。
    你相信吗？一英里后， 所有的 {\em 6} 位数字都变成了回文数。

    那么问题来了， 当我第一次看到里程表时， 里程数是多少?''
\end{quote}

%🍁% Write a Python program that tests all the six-digit numbers and prints
%🍁% any numbers that satisfy these requirements.
%🍁% Solution: \url{http://thinkpython2.com/code/cartalk2.py}.

编写写一个程序， 测试所有的 {\em 6} 位数字， 然后输出所有符合要求的结果。
\href{http://thinkpython2.com/code/cartalk2.py}{参考答案}

\end{exercise}


\begin{exercise}
%🍁% Here's another {\em Car Talk} Puzzler you can solve with a
%🍁% search (\url{http://www.cartalk.com/content/puzzlers}):

还是 {\em 《\href{http://www.cartalk.com/content/puzzlers}{Car Talk}》} 的谜题， 你可以通过利用搜索模式解答：
\index{Car Talk}  \index{Puzzler}  \index{palindrome}

%🍁% \begin{quote}
%🍁% ``Recently I had a visit with my mom and we realized that
%🍁% the two digits that make up my age when reversed resulted in her
%🍁% age. For example, if she's 73, I'm 37. We wondered how often this has
%🍁% happened over the years but we got sidetracked with other topics and
%🍁% we never came up with an answer.
%🍁%
%🍁% When I got home I figured out that the digits of our ages have been
%🍁% reversible six times so far. I also figured out that if we're lucky it
%🍁% would happen again in a few years, and if we're really lucky it would
%🍁% happen one more time after that. In other words, it would have
%🍁% happened 8 times over all. So the question is, how old am I now?''
%🍁%
%🍁% \end{quote}

\begin{quote}

    ``最近我探望了我的妈妈， 我们忽然意识到把我的年纪数字反过来就是她的年龄。  比如， 如果她 {\em 73} 岁， 那么我就是 {\em 37} 岁。
    我们想知道过去这些年来， 发生了多少次这样的巧合， 但是我们很快偏离到其他话题上， 最后并没有找到答案。

    回到家后， 我计算出我的年龄数字有 {\em 6} 次反过来就是妈妈的年龄。
    同时， 我也发现如果幸运的话， 将来几年还可能发生这样的巧合，
    运气再好点的话， 之后还会出现一次这样的巧合。
    换句话说， 这样的巧合一共会发生8次。  那么， 问题来了， 我现在多大了？''

\end{quote}

%🍁% Write a Python program that searches for solutions to this Puzzler.
%🍁% Hint: you might find the string method {\tt zfill} useful.
%🍁%
%🍁% Solution: \url{http://thinkpython2.com/code/cartalk3.py}.

编写一个查找谜题答案的 {\em Python} 函数\footnote{提示：字符串的 \li{zfill} 方法特别有用。  }。
\href{http://thinkpython2.com/code/cartalk3.py}{答案}


\end{exercise}
